package com.blm.service.impl;


import com.blm.common.Message;
import com.blm.common.constants.CacheConstant;
import com.blm.common.constants.ExceptionConstant;
import com.blm.dto.*;
import com.blm.entity.*;
import com.blm.exception.BusinessException;
import com.blm.exception.CommonException;
import com.blm.exception.NotFoundException;
import com.blm.handler.WebSocketHandler;
import com.blm.repository.*;
import com.blm.service.OrderService;
import com.blm.service.StoreService;
import com.blm.service.UserService;
import com.blm.util.RedisUtil;
import com.blm.vo.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private CartRepository cartRepository;
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private OrderDetailRepository detailRepository;
    
    @Autowired
    private FoodRepository foodRepository;
    
    @Autowired
    private StoreRepository storeRepository;
    
    @Autowired
    private UserAddressRepository addressRepository;
    
    @Autowired
    private ReviewRepository reviewRepository;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private StoreService storeService;

    @Autowired
    private RiderRepository riderRepository;

    @Autowired
    private UserService userService;

    @Autowired @Lazy
    private OrderServiceImpl self;

    @Override
    @Transactional
    @Caching(evict = {
            @CacheEvict(value = CacheConstant.CART_ALL, key = "#userId"),
            @CacheEvict(value = CacheConstant.CART_STORE, key = "#userId + '_' + #dto.storeId"),
    })
    public OrderVO createOrder(Long userId, OrderCreateDTO dto) {
        // 获取购物车
        List<Cart> carts = cartRepository.findByUserStore(userId, dto.getStoreId());
        if (carts.isEmpty()) {
            throw new CommonException(ExceptionConstant.REQ_PARAM_ERROR);
        }
        
        // 检查地址是否存在
        addressRepository.findByIdAndUserId(dto.getAddressId(), userId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.USER_ADDRESS_NOT_FOUND));
        
        // 检查店铺是否营业
        Store store = storeRepository.findByIdAndStatus(dto.getStoreId(), Store.StoreStatus.OPEN)
                .orElseThrow(() -> new CommonException(ExceptionConstant.STORE_CLOSED));
        
        // 生成订单
        Order order = new Order();
        String orderNo = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16);
        order.setOrderNo(orderNo);
        order.setUserId(userId);
        order.setStoreId(dto.getStoreId());
        order.setAddressId(dto.getAddressId());
        order.setRemark(dto.getRemark());
        order.setStatus(Order.OrderStatus.ORDER_CREATED);
        order.setCreatedAt(LocalDateTime.now());
        order.setUpdatedAt(LocalDateTime.now());
        
        // 计算金额
        BigDecimal total = BigDecimal.ZERO;
        BigDecimal deliveryFee = store.getDeliveryFee();
        
        for (Cart c : carts) {
            Food food = foodRepository.findByIdAndStatus(c.getFoodId(), Food.FoodStatus.ON_SHELF)
                    .orElseThrow(() -> new CommonException(ExceptionConstant.FOOD_STATUS_ERROR));
            
            BigDecimal price = food.getPrice();
            BigDecimal amount = price.multiply(BigDecimal.valueOf(c.getQuantity()));
            total = total.add(amount);
        }
        
        // 检查是否达到最低订单金额
        if (total.compareTo(store.getMinOrderAmount()) < 0) {
            throw BusinessException.of(ExceptionConstant.ORDER_AMOUNT_BELOW.getCode(), "未达到店铺最低订单金额 " + store.getMinOrderAmount() + "元");
        }
        
        order.setTotalAmount(total);
        order.setDeliveryFee(deliveryFee);
        order.setDiscountAmount(BigDecimal.ZERO);  // todo:此处可以添加优惠逻辑
        order.setPaymentAmount(total.add(deliveryFee));
        orderRepository.insert(order);

        // 保存明细并清空对应购物车项
        for (Cart c : carts) {
            Food food = foodRepository.findById(c.getFoodId()).orElse(new Food());

            OrderDetail detail = new OrderDetail();
            detail.setOrderId(order.getId());
            detail.setFoodId(c.getFoodId());
            detail.setFoodName(food.getName());
            detail.setFoodImage(food.getImage());
            detail.setPrice(food.getPrice());
            detail.setQuantity(c.getQuantity());
            detail.setAmount(food.getPrice().multiply(BigDecimal.valueOf(c.getQuantity())));
            detail.setCreatedAt(LocalDateTime.now());
            detailRepository.insert(detail);

            cartRepository.deleteByIdAndUserId(c.getId(), userId);
        }

        // 返回VO
        OrderVO vo = new OrderVO();
        BeanUtils.copyProperties(order, vo);
        vo.setStoreName(store.getName());
        redisUtil.clearCache(CacheConstant.ORDERS_STORE, dto.getStoreId());
        redisUtil.clearCache(CacheConstant.ORDERS_USER, userId);
        return vo;
    }

    private List<OrderVO> order2vo(List<Order> orders) {
        return orders.stream().map(order -> {
            Store store;
            try {
                store = storeService.getStoreById(order.getStoreId());
            } catch (Exception ignore) {
                store = new Store();
            }

            OrderVO vo = new OrderVO();
            BeanUtils.copyProperties(order, vo);
            vo.setStoreName(store.getName());
            vo.setStoreImage(store.getLogo());
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    @Cacheable(value = CacheConstant.ORDERS_STORE, key = "#storeId + '_' + #status + '_' + #page + '_' + #size")
    public PageVO<OrderVO> listStoreOrders(Long storeId, Order.OrderStatus status, int page, int size) {
        // 使用PageHelper进行分页查询
        PageHelper.startPage(page, size);

        List<Order> orders;
        if (status != null) {
            orders = orderRepository.findByStoreIdAndStatus(storeId, status);
        } else {
            orders = orderRepository.findAllByStoreId(storeId);
        }

        return getPageVO(page, size, orders);
    }

    @Override
    @Cacheable(value = CacheConstant.ORDERS_USER, key = "#userId + '_' + #status + '_' + #page + '_' + #size")
    public PageVO<OrderVO> listUserOrders(Long userId, Order.OrderStatus status, int page, int size) {
        // 使用PageHelper进行分页查询
        PageHelper.startPage(page, size);
        
        List<Order> orders;
        if (status != null) {
            orders = orderRepository.findByUserIdAndStatus(userId, status);
        } else {
            orders = orderRepository.findAllByUserId(userId);
        }

        return getPageVO(page, size, orders);
    }

    private PageVO<OrderVO> getPageVO(int page, int size, List<Order> orders) {
        // 获取分页信息
        Page<Order> pageInfo = (Page<Order>) orders;
        // 转换为VO列表
        List<OrderVO> orderVOs = order2vo(orders);
        // 创建Spring分页对象
        return new PageVO<>(page, size, pageInfo.getTotal(), pageInfo.getPages(), orderVOs);
    }

    @Override
    public OrderDetailVO getOrderDetail(Long userId, Long orderId) {
        Order order = orderRepository.findByIdAndUserId(orderId, userId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.ORDER_NOT_FOUND));

        return self.getOrderDetailVOFromOrder(order);
    }

    @Override
    @Transactional
    @CacheEvict(value = CacheConstant.ORDER_DETAIL, key = "#orderId")
    public void cancelOrder(Long userId, Long orderId) {
        Order order = orderRepository.findByIdAndUserId(orderId, userId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.ORDER_NOT_FOUND));
                
        if (order.getStatus().compareTo(Order.OrderStatus.PENDING) > 0) {
            throw new CommonException(ExceptionConstant.ORDER_STATUS_ERROR);
        }

        int updated = orderRepository.updateStatus(orderId, userId, Order.OrderStatus.CANCELLED, LocalDateTime.now());
        // todo: 退款逻辑
        if (updated != 1) {
            throw new CommonException(ExceptionConstant.SYS_DATABASE_ERROR);
        }
        redisUtil.clearCache(CacheConstant.ORDERS_STORE, order.getStoreId());
        redisUtil.clearCache(CacheConstant.ORDERS_USER, userId);
    }

    @Override
    @Transactional
    @CacheEvict(value = CacheConstant.ORDER_DETAIL, key = "#orderId")
    public PaymentResultVO payOrder(Long userId, Long orderId, PaymentDTO.PaymentType paymentType) {
        Order order = orderRepository.findByIdAndUserId(orderId, userId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.ORDER_NOT_FOUND));
        Long merchantId = storeService.getStoreOwnerId(order.getStoreId());
                
        if (!order.getStatus().equals(Order.OrderStatus.ORDER_CREATED)) {
            throw new CommonException(ExceptionConstant.ORDER_STATUS_ERROR);
        }
        
        // 这里应该调用实际的支付网关
        // 支付成功后更新订单状态
        int insertRes = orderRepository.updatePaymentType(orderId, userId, paymentType, LocalDateTime.now());
        int updateRes = orderRepository.updateStatus(orderId, userId, Order.OrderStatus.PENDING, LocalDateTime.now());

        if (updateRes + insertRes != 2) {
            throw new CommonException(ExceptionConstant.SYS_DATABASE_ERROR);
        }

        // 通知商家
        WebSocketHandler.notify(merchantId, Message.MessageType.MERCHANT_ORDER_CREATE, order.getOrderNo());

        PaymentResultVO res = new PaymentResultVO();
        res.setOrderId(orderId);
        res.setOrderNo(order.getOrderNo());
        res.setPaymentStatus(Order.PAID);
        res.setPaymentMessage("支付成功");
        res.setPaymentAmount(order.getPaymentAmount());
        res.setPaymentTime(LocalDateTime.now());

        redisUtil.clearCache(CacheConstant.ORDERS_STORE, order.getStoreId());
        redisUtil.clearCache(CacheConstant.ORDERS_USER, userId);
        return res;
    }
    
    @Override
    @Transactional
    @CacheEvict(value = CacheConstant.ORDER_DETAIL, key = "#dto.orderId")
    public PaymentResultVO simulatePayment(Long userId, PaymentDTO dto) {
        return payOrder(userId, dto.getOrderId(), dto.getPaymentType());
    }
    
    @Override
    @Transactional
    public void urgeOrder(Long userId, Long orderId) {
        Order order = orderRepository.findByIdAndUserId(orderId, userId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.ORDER_NOT_FOUND));

        Long merchantId = storeService.getStoreOwnerId(order.getStoreId());
        Long riderId = order.getRiderId();
        // 如果已经完成或取消
        if (order.getStatus().compareTo(Order.OrderStatus.COMPLETED) >= 0) {
            throw new CommonException(ExceptionConstant.ORDER_STATUS_ERROR);
        }

        // 催单
        // 实际逻辑：记录催单记录，通知商家或骑手
        // 这里简化为检查状态即可
        Map<String, Object> urgeData = Map.of(
                "orderId", orderId,
                "orderNo", order.getOrderNo(),
                "userId", userId
        );
        WebSocketHandler.notify(merchantId, Message.MessageType.USER_ORDER_URGENT, urgeData);
        if(riderId != null) {
            Long riderUserid = riderRepository.findById(riderId).get().getUserId();
            WebSocketHandler.notify(riderUserid, Message.MessageType.USER_ORDER_URGENT, urgeData);
        }
    }
    
    @Override
    @Transactional
    @CachePut(value = CacheConstant.ORDER_DETAIL, key = "#orderId")
    public OrderDetailVO confirmReceipt(Long userId, Long orderId) {
        Order order = orderRepository.findByIdAndUserId(orderId, userId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.ORDER_NOT_FOUND));

        if (!order.getStatus().equals(Order.OrderStatus.DELIVERING) &&
            !order.getStatus().equals(Order.OrderStatus.DELIVERED)) {
            throw new CommonException(ExceptionConstant.ORDER_STATUS_ERROR);
        }

        int updated = orderRepository.updateStatus(orderId, userId, Order.OrderStatus.COMPLETED, LocalDateTime.now());
        if (updated != 1) {
            throw new CommonException(ExceptionConstant.SYS_DATABASE_ERROR);
        }

        redisUtil.clearCache(CacheConstant.ORDERS_STORE, order.getStoreId());
        redisUtil.clearCache(CacheConstant.ORDERS_USER, userId);
        return getOrderDetail(userId, orderId);
    }
    
    @Override
    @Transactional
    @CacheEvict(value = CacheConstant.FOOD_DETAIL, allEntries = true)
    public ReviewVO addReview(Long userId, Long orderId, ReviewDTO reviewDTO) {
        Order order = orderRepository.findByIdAndUserId(orderId, userId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.ORDER_NOT_FOUND));
                
        if (!order.getStatus().equals(Order.OrderStatus.COMPLETED)) {
            throw new CommonException(ExceptionConstant.ORDER_STATUS_ERROR);
        }
        
        // 检查是否已评价
        if (reviewRepository.existsByOrderId(orderId)) {
            throw new CommonException(ExceptionConstant.REVIEW_ALREADY_EXISTS);
        }
        
        Review review = new Review();
        BeanUtils.copyProperties(reviewDTO, review);
        BeanUtils.copyProperties(order, review);
        review.setCreatedAt(LocalDateTime.now());
        review.setUpdatedAt(LocalDateTime.now());
        review.setId(null);

        reviewRepository.save(review);

        // todo: 更新店铺评分（实际应该有更复杂的计算逻辑）
        // 此处简化处理

        ReviewVO vo = new ReviewVO();
        BeanUtils.copyProperties(review, vo);
        redisUtil.clearCache(CacheConstant.REVIEWS, order.getStoreId());
        return vo;
    }

    /**
     * 商家用于更新订单状态 (例如: `CONFIRMED` - 已接单, `PREPARING` - 备餐中, `READY_FOR_PICKUP` - 备餐完成)
     */
    @Override
    @Transactional
    @CacheEvict(value = CacheConstant.ORDER_DETAIL, key = "#orderId")
    public void updateOrderStatusByStore(Long orderId, Long storeId, OrderStatusUpdateDTO dto,User user) {
        Order order = orderRepository.findByIdAndStoreId(orderId, storeId)
                .orElseThrow(()->new NotFoundException(ExceptionConstant.ORDER_NOT_FOUND));
        OrderTracking orderTracking = new OrderTracking();
        orderTracking.setOrderId(orderId);
        orderTracking.setStatus(dto.getOrderStatus());
        orderTracking.setCreatedAt(LocalDateTime.now());
        orderTracking.setOperatorId(user.getId());
        orderTracking.setOperatorType(user.getRole());
        orderTracking.setStatus(dto.getOrderStatus());

        orderRepository.insertOrderTraceByMerchant(orderTracking);
        orderRepository.updateStatusByStore(orderId, storeId, orderTracking.getStatus(), LocalDateTime.now());
        redisUtil.clearCache(CacheConstant.ORDERS_STORE, order.getStoreId());
        redisUtil.clearCache(CacheConstant.ORDERS_USER, order.getUserId());
    }

    @Override
    public OrderDetailVO getOrderDetailwithStore(Long orderId,Long storeId) {
        Order order = orderRepository.findByIdAndStoreId(orderId, storeId)
                    .orElseThrow(() -> new CommonException(ExceptionConstant.ORDER_NOT_FOUND));

        return self.getOrderDetailVOFromOrder(order);
    }

    @Override
    public OrderDetailVO getOrderDetail(Long orderId) {
        Order order = orderRepository.findById(orderId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.ORDER_NOT_FOUND));

        return self.getOrderDetailVOFromOrder(order);
    }

    @Cacheable(value = CacheConstant.ORDER_DETAIL, key = "#order.id")
    public OrderDetailVO getOrderDetailVOFromOrder(Order order) {
        Store store;
        try {
            store = storeService.getStoreById(order.getStoreId());
        } catch (Exception ignore) {
            store = new Store();
        }

        UserAddress address = addressRepository.findById(order.getAddressId()).orElse(null);

        OrderDetailVO vo = new OrderDetailVO();
        BeanUtils.copyProperties(order, vo);
        vo.setStoreName(store.getName());

        if (address != null) {
            UserAddressVO addressVO = new UserAddressVO();
            BeanUtils.copyProperties(address, addressVO);
            vo.setDeliveryAddress(addressVO);
        }

        if (order.getRiderId() != null) {
            Rider rider = riderRepository.findById(order.getRiderId())
                    .orElseThrow(() -> new CommonException(ExceptionConstant.RIDER_NOT_FOUND));
            vo.setRiderName(rider.getRealName());
            User rider_user = userService.findUserById(rider.getUserId());
            vo.setRiderPhone(rider_user.getPhone());
        }

        List<OrderItemVO> items = detailRepository.findByOrderId(order.getId()).stream().map(d -> {
            OrderItemVO ivo = new OrderItemVO();
            BeanUtils.copyProperties(d, ivo);
            return ivo;
        }).collect(Collectors.toList());

        vo.setItems(items);
        return vo;
    }

    @Override
    @Cacheable(value = CacheConstant.RIDER_LOCATION, key = "#orderId")
    public LocationUpdateDTO getRiderLocation(Long userId, Long orderId) {
        Order order = orderRepository.findByIdAndUserId(orderId, userId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.ORDER_NOT_FOUND));
        if (order.getRiderId() == null) {
            throw new CommonException(ExceptionConstant.RIDER_NOT_FOUND);
        }
        Rider rider = riderRepository.findById(order.getRiderId())
                .orElseThrow(() -> new CommonException(ExceptionConstant.RIDER_NOT_FOUND));
        LocationUpdateDTO dto = new LocationUpdateDTO();
        dto.setLatitude(rider.getLatitude());
        dto.setLongitude(rider.getLongitude());

        return dto;
    }
}